#include <dim-sum/beehive.h>
#include <dim-sum/amba/bus.h>
#include <dim-sum/amba/serial.h>
#include <dim-sum/errno.h>
#include <dim-sum/init.h>
#include <dim-sum/irq.h>
#include <dim-sum/irq_mapping.h>
#include <dim-sum/serial.h>
#include <dim-sum/serial_core.h>
#include <dim-sum/workqueue.h>
#include <asm/memory.h>
#include <asm/io.h>

#include <dim-sum/linkage.h>
#include <dim-sum/types.h>

#include <dim-sum/cache.h>
#include <dim-sum/devtree.h>
#include <dim-sum/printk.h>
#include <dim-sum/delay.h>
#include <asm/early_map.h>
#include <asm/io.h>
#include <dim-sum/serial_reg.h>
#include "ns16550a.h"

#define AMBA_ISR_PASS_LIMIT	256
#define UART_DUMMY_DR_RX	(1 << 16)


#define UART_DR_ERROR		(UART011_DR_OE|UART011_DR_BE|UART011_DR_PE|UART011_DR_FE)

static struct uart_driver ns_16550a_reg;
/*
 * We wrap our port structure around the generic uart_port.
 */
struct uart_16550a_port {
	struct uart_port	port;
	struct clk		*clk;
	const struct vendor_data *vendor;
	unsigned int		im;		/* interrupt mask */
	unsigned int		old_status;
	u32 reg_shift;
	u32 reg_width;
	u32 baudrate;
	u32 lcr_last;
	u32 ier;
	struct work_struct tx_softirq_work;
	bool			autorts;
	unsigned int		tx_irq_seen;	/* 0=none, 1=1, 2=2 or more */
	char			type[12];
};

/* There is by now at least one vendor with differing details, so handle it */
struct vendor_data {
	unsigned int		ifls;
	unsigned int		lcrh_tx;
	unsigned int		lcrh_rx;
	bool			oversampling;
	bool			dma_threshold;
	bool			cts_event_workaround;

	unsigned int (*get_fifosize)(struct amba_device *dev);
};

#define UART_NR			14

static struct uart_16550a_port *amba_ports[UART_NR];
static struct uart_16550a_port *test_uap = NULL;

static u8 ns16550a_read(struct uart_16550a_port *uap, u32 offset)
{
	u8 ret;

	switch (uap->reg_width) {
	case 4:
		ret = readl(uap->port.membase + (offset << uap->reg_shift));
		break;
	case 2:
		ret = readw(uap->port.membase + (offset << uap->reg_shift));
		break;
	default:
		ret = readb(uap->port.membase + (offset << uap->reg_shift));
		break;
	};

	return ret;
}
static void ns16550a_write(struct uart_16550a_port *uap, u32 offset, u8 val)
{
	switch (uap->reg_width) {
	case 4:
		writel(val, uap->port.membase + (offset << uap->reg_shift));
		break;
	case 2:
		writew(val, uap->port.membase + (offset << uap->reg_shift));
		break;
	default:
		writeb(val, uap->port.membase + (offset << uap->reg_shift));
		break;
	};

	if (offset == UART_LCR_OFFSET)
		uap->lcr_last = val;
}

/*
 * Transmit a character
 *
 * Returns true if the character was successfully queued to the FIFO.
 * Returns false otherwise.
 */
static bool ns_16550a_tx_char(struct uart_16550a_port *uap, unsigned char c)
{
	while (!(ns16550a_read(uap, UART_LSR_OFFSET) & UART_LSR_THRE));
	ns16550a_write(uap, UART01x_DR, c);
	uap->port.icount.tx++;
	return true;
}

#define ns_16550a_dma_flush_buffer	NULL

static void ns_16550a_stop_tx(struct uart_port *port, unsigned int tty_stop)
{

}

static bool ns_16550a_tx_chars(struct uart_16550a_port *uap)
{
	struct circ_buf *xmit = &uap->port.info->xmit;

	while (ns_16550a_tx_char(uap, xmit->buf[xmit->tail])) {
		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
		if (uart_circ_empty(xmit))
			break;
	}

	return false;
}

static unsigned int ns_16550a_tx_empty(struct uart_port *port)
{
	struct uart_16550a_port *uap =
	    container_of(port, struct uart_16550a_port, port);
	unsigned int status = ns16550a_read(uap, UART01x_FR);
	return status & (UART01x_FR_BUSY|UART01x_FR_TXFF) ? 0 : TIOCSER_TEMT;
}

/* Start TX with programmed I/O only (no DMA) */
static void ns_16550a_start_tx_pio(struct uart_16550a_port *uap)
{
	ns_16550a_tx_chars(uap);
}

static void ns_16550a_start_tx(struct uart_port *port, unsigned int tty_start)
{
	struct uart_16550a_port *uap =
	    container_of(port, struct uart_16550a_port, port);
	ns_16550a_start_tx_pio(uap);
}

static void ns_16550a_stop_rx(struct uart_port *port)
{

}

static void ns_16550a_enable_ms(struct uart_port *port)
{

}

static void ns_16550a_break_ctl(struct uart_port *port, int break_state)
{

}

/*
 * Reads up to 256 characters from the FIFO or until it's empty and
 * inserts them into the TTY layer. Returns the number of characters
 * read from the FIFO.
 */
static int ns_16550a_fifo_to_tty(struct uart_16550a_port *uap, char ch)
{
	uart_insert_char(&uap->port, ch, UART011_DR_OE, ch, TTY_NORMAL);
	return 0;
}

static void ns_16550a_rx_chars(struct uart_16550a_port *uap, char ch)
{
	ns_16550a_fifo_to_tty(uap, ch);
}


static void ns16550a_clear_errors(struct uart_16550a_port *uap)
{
	/**
	 * 如果有一个RX FIFO错误，继续从RX FIFO移除条目直到LSR不显示这个比特的设置
	 */
	while (ns16550a_read(uap, UART_LSR_OFFSET) & UART_LSR_BRK_ERROR_BITS) {
		ns16550a_read(uap, UART_RBR_OFFSET);
	}
}

static enum isr_result ns_16550a_int(unsigned int irq, void *dev_id)
{
	struct uart_16550a_port *uap = dev_id;
	int handled = 0;
	u16 iir, lsr;

	smp_lock(&uap->port.lock);

	iir = ns16550a_read(uap, UART_IIR_OFFSET);
	lsr = ns16550a_read(uap, UART_LSR_OFFSET);

	switch (iir & 0xf) {

	case UART_IIR_RLSI:
	case UART_IIR_RTO:
	case UART_IIR_RDI:
		if (lsr & UART_LSR_BRK_ERROR_BITS)
			ns16550a_clear_errors(uap);

		if (lsr & UART_LSR_DR) {
			do {
				char ch = ns16550a_read(uap, UART_RBR_OFFSET);
				ns_16550a_rx_chars(uap, ch);
			} while (ns16550a_read(uap,UART_LSR_OFFSET) & (UART_LSR_DR | UART_LSR_OE));

			smp_unlock(&uap->port.lock);
			tty_flip_buffer_push(uap->port.info->tty);
			smp_lock(&uap->port.lock);
		}
			
		break;

	case UART_IIR_BUSY:
		ns16550a_read(uap, 0x1f);
		ns16550a_write(uap, UART_LCR_OFFSET, uap->lcr_last);
		break;
	default:
		break;
	};

	smp_unlock(&uap->port.lock);

	return handled ? ISR_EATEN : ISR_SKIP;
}


static int ns_16550a_startup(struct uart_port *port)
{
	struct uart_16550a_port *uap = container_of(port, struct uart_16550a_port, port);
		/* 使能接收中断 */
	uap->ier |= (UART_IER_RLSI | UART_IER_RDI);
	ns16550a_write(uap, UART_IER_OFFSET, uap->ier);
	register_isr_handle(uap->port.irq , ns_16550a_int, 0, "uart-16550a", uap);
	test_uap = uap;
	return 0;
}


static void
ns_16550a_set_termios(struct uart_port *port, struct termios *termios,
		     struct termios *old)
{

}


static const char *ns_16550a_type(struct uart_port *port)
{
	return NULL;
}

/*
 * Release the memory region(s) being used by 'port'
 */
static void release_16550a_port(struct uart_port *port)
{
}

/*
 * Request the memory region(s) being used by 'port'
 */
static int request_16550a_port(struct uart_port *port)
{
	return 0;
}

/*
 * Configure/autoconfigure the port.
 */
static void config_16550a_port(struct uart_port *port, int flags)
{
	if (flags & UART_CONFIG_TYPE) {
		port->type = PORT_16550A;
	}
}

/*
 * verify the new serial_struct (for TIOCSSERIAL).
 */
static int ns_16550a_verify_port(struct uart_port *port, struct serial_struct *ser)
{
	int ret = 0;

	return ret;
}

static void ns_16550a_set_mctrl(struct uart_port *port, unsigned int mctrl)
{

}

static unsigned int ns_16550a_get_mctrl(struct uart_port *port)
{
	return 0;
}

static struct uart_ops amba_ns_16550a_pops = {
	.tx_empty	= ns_16550a_tx_empty,
	.set_mctrl	= ns_16550a_set_mctrl,
	.get_mctrl	= ns_16550a_get_mctrl,
	.stop_tx	= ns_16550a_stop_tx,
	.start_tx	= ns_16550a_start_tx,
	.stop_rx	= ns_16550a_stop_rx,
	.enable_ms	= ns_16550a_enable_ms,
	.break_ctl	= ns_16550a_break_ctl,
	.startup	= ns_16550a_startup,
	.flush_buffer	= ns_16550a_dma_flush_buffer,
	.set_termios	= ns_16550a_set_termios,
	.type		= ns_16550a_type,
	.release_port	= release_16550a_port,
	.request_port	= request_16550a_port,
	.config_port	= config_16550a_port,
	.verify_port	= ns_16550a_verify_port,
};

static void ns16550a_lowlevel_init(struct uart_16550a_port *port)
{
	ns16550a_write(port, UART_LCR_OFFSET, 0x80);
	ns16550a_write(port, UART_LCR_OFFSET, 0x03);
	ns16550a_write(port, UART_FCR_OFFSET, 0x01);
	ns16550a_write(port, UART_MCR_OFFSET, 0x00);
	ns16550a_read(port, UART_LSR_OFFSET);
	ns16550a_read(port, UART_RBR_OFFSET);
	ns16550a_write(port, UART_SCR_OFFSET, 0x00);
	port->ier = 0x00;
	ns16550a_write(port, UART_IER_OFFSET, 0x00);
}

static struct irq_configure ns_16550a_of_desc[] =
{
	[0] = {3, {0, 0xa, 4}, NULL},
};

static int ns_16550a_probe(struct device *dev)
{
	struct uart_16550a_port *uap;
	int i, ret;
	uint64_t size;
	u32 interrupt = 0;
	if (dt_read_u32(dev->dt_node, "interrupts", &interrupt))
		return -EBUSY;
	
	ns_16550a_of_desc[0].args[1] = interrupt;

	init_one_hwirq(&ns_16550a_of_desc[0]);
	
	for (i = 0; i < ARRAY_SIZE(amba_ports); i++)
		if (amba_ports[i] == NULL)
			break;

	if (i == ARRAY_SIZE(amba_ports))
		return -EBUSY;

	uap = kzalloc(sizeof(struct uart_16550a_port),
			   PAF_KERNEL);
	if (uap == NULL)
		return -ENOMEM;

	uap->ier = 0;
	uap->port.dev = dev;
	uap->port.mapbase = dt_get_addr(dev->dt_node, &size, 0);
	uap->port.membase = dt_reg_map(dev->dt_node, 0);
	uap->port.iotype = UPIO_MEM;
	uap->port.irq = interrupt;
	uap->port.ops = &amba_ns_16550a_pops;
	uap->port.flags = UPF_BOOT_AUTOCONF;
	uap->port.line = i;
	uap->port.type = PORT_16550A;
	if (dt_read_u32(dev->dt_node, "reg-shift", &uap->reg_shift))
		uap->reg_shift = 0;

	if (dt_read_u32(dev->dt_node, "reg-io-width", &uap->reg_width))
		uap->reg_width = 1;

	uap->port.regshift = uap->reg_shift;

	uap->baudrate = 115200;
	amba_ports[i] = uap;

	ns16550a_lowlevel_init(uap);

	dev_set_drvdata(dev, uap);

	if (!ns_16550a_reg.state) {
		ret = uart_register_driver(&ns_16550a_reg);
		if (ret < 0) {
			return ret;
		}
	}

	ret = uart_add_one_port(&ns_16550a_reg, &uap->port);
	
	if (ret) {
		amba_ports[i] = NULL;
		uart_unregister_driver(&ns_16550a_reg);
	}

	return ret;
}

static u8 ns16550a_uart_port_read(struct uart_port *port, u32 offset)
{
	u8 ret;

	switch (port->iotype) {
	case UPIO_MEM32:
		ret = readl(port->membase + (offset << port->regshift));
		break;
	default:
		ret = readb(port->membase + (offset << port->regshift));
		break;
	};

	return ret;
}

static void ns16550a_uart_port_write(struct uart_port *port, u32 offset, u8 val)
{
	switch (port->iotype) {
	case UPIO_MEM32:
		writel(val, port->membase + (offset << port->regshift));
		break;
	default:
		writeb(val, port->membase + (offset << port->regshift));
		break;
	};
}

static void ns_16550a_console_putchar(struct uart_port *port, int ch)
{
	while (!(ns16550a_uart_port_read(port, UART_LSR_OFFSET) & UART_LSR_THRE));
	ns16550a_uart_port_write(port, (UART_THR_OFFSET), (u8)ch);
}

static void
ns_16550a_console_write(struct console *co, const char *s, unsigned int count)
{
	struct uart_16550a_port *uap = amba_ports[co->index];

	uart_console_write(&uap->port, s, count, ns_16550a_console_putchar);
}

static struct tty_driver *uart_console_device(struct console *co, int *index)
{
	struct uart_driver *p = co->data;
	*index = co->index;
	return p->tty_driver;
}

#define SERIAL_AMBA_MAJOR	204
#define SERIAL_AMBA_MINOR	64

static struct console console_16550a = {
	.name		= "ttyAMA",
	.write		= ns_16550a_console_write,
	.device		= uart_console_device,
	.flags		= CONSOLE_PRINTK,
	.index		= -1,
	.data		= &ns_16550a_reg,
};

#define CONSOLE_16550a	(&console_16550a)

static struct uart_driver ns_16550a_reg = {
	//.owner			= THIS_MODULE,
	.driver_name		= "ttyNS",
	.dev_name		= "ttyNS",
	.sysobj_name		= "ttyNS",
	.major			= SERIAL_AMBA_MAJOR,
	.minor			= SERIAL_AMBA_MINOR,
	.nr			= UART_NR,
	.cons			= CONSOLE_16550a,
};

static const struct dt_device_id ns_16550a_dt_ids[] = {
	{
		.compatible = "ns16550a",
	},
	{ }
};

struct device_driver ns16550a_uart_driver = {
	.name	= "ns-16550a",
	.match_table = ns_16550a_dt_ids,
	.probe	= ns_16550a_probe,
};
